package com.znjc.iot.serial;

import cn.hutool.extra.spring.SpringUtil;
import com.znjc.iot.domain.IotSerial;
import com.znjc.iot.exception.*;
import com.znjc.iot.listener.PortListener;
import com.znjc.iot.service.IotService;
import com.znjc.iot.util.DataUtil;
import gnu.io.SerialPort;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.TooManyListenersException;

/**
 * 串口实体类
 */
@Slf4j
public class SerialPortUtil {

    private IotSerial iotSerial; // 新增字段
    // Getter/Setter
    public IotSerial getIotSerial() {
        return iotSerial;
    }
    public void setIotSerial(IotSerial iotSerial) {
        this.iotSerial = iotSerial;
    }
    /**
     * 串口号
     */
    String name;
    /**
     * 串口实体
     */
    SerialPort serialPort;

    public SerialPortUtil(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    public SerialPort getSerialPort() {
        return serialPort;
    }

    public void setSerialPort(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    IotService iotService;

    public IotService getIotService() {
        if (iotService == null) {
            iotService = SpringUtil.getBean(IotService.class);
        }
        return iotService;
    }

    /**
     * 发送数据到串口
     *
     * @param data
     * @throws SendDataToSerialPortFailure
     * @throws SerialPortOutputStreamCloseFailure
     */
    public void sendData(byte[] data) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {
        OutputStream out = null;
        try {
            out = serialPort.getOutputStream();
            out.write(data);
            out.flush();
        } catch (IOException e) {
            log.error("发送数据,在写数据时发生异常:{}", e.getMessage());
            throw new SendDataToSerialPortFailure();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                log.error("发送数据,在关闭流时发送异常:{}", e.getMessage());
                throw new SerialPortOutputStreamCloseFailure();
            }
        }

    }

    /**
     * 发送命令
     * @param data
     * @throws SendDataToSerialPortFailure
     * @throws SerialPortOutputStreamCloseFailure
     */
    public void sendCommand(byte[] data) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {
        log.info("发送指令:"+DataUtil.printHexString(data));
        OutputStream out = null;
        try {
            out = serialPort.getOutputStream();
            out.write(data);
            out.flush();
        } catch (IOException e) {
            log.error("发送数据,在写数据时发生异常:{}", e.getMessage());
            throw new SendDataToSerialPortFailure();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                log.error("发送数据,在关闭流时发送异常:{}", e.getMessage());
                throw new SerialPortOutputStreamCloseFailure();
            }
        }
    }

    /**
     * 从串口读取数据
     *
     * @return
     * @throws ReadDataFromSerialPortFailure
     * @throws SerialPortInputStreamCloseFailure
     */
    public void readData(SerialPortUtil serialPortVo, Timestamp date) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {
        InputStream in = null;
        byte[] readBuffer = null;
        try {
            in = serialPort.getInputStream();
            // 获取buffer里的数据长度
            int buffLength = in.available();
            while (buffLength > 0) {
                readBuffer = readFromPort();
                String needData = DataUtil.printHexString(readBuffer);
                log.info(serialPortVo.getName() + "串口接收到数据: " + needData);
                getIotService().analyzeData(needData, date);
                buffLength = in.available();
            }
        } catch (IOException e) {
            log.error("读取数据时异常:{}", e.getMessage());
            throw new ReadDataFromSerialPortFailure();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("读取数据,在关闭流时发生异常:{}", e.getMessage());
                throw new SerialPortInputStreamCloseFailure();
            }
        }
    }

    public byte[] readFromPort() {
        InputStream in = null;
        byte[] bytes = {};
        try {
            in = serialPort.getInputStream();
            // 缓冲区大小为一个字节
            byte[] readBuffer = new byte[1];
            int bytesNum = in.read(readBuffer);
            while (bytesNum > 0) {
                bytes = DataUtil.concat(bytes, readBuffer);
                bytesNum = in.read(readBuffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                    in = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    /**
     * 绑定监听器
     *
     * @param listener
     * @param sleepTime
     * @throws TooManyListeners
     */
    public void bindListener(PortListener listener, int sleepTime) throws TooManyListeners {
        try {
            log.info("开始绑定监听器");
            //设置监听器
            listener.setSerialPort(this);
            log.info("设置休眠时间");
            listener.setSleepTime(sleepTime);
            // 给串口添加监听器
            log.info("给串口添加监听器");
            serialPort.addEventListener(listener);
            // 设置当有数据到达时唤醒监听接收线程
            log.info("设置当有数据到达时唤醒监听接收线程");
            serialPort.notifyOnDataAvailable(true);
            // 设置当通信中断时唤醒中断线程
            log.info("设置当通信中断时唤醒中断线程");
            serialPort.notifyOnBreakInterrupt(true);
        } catch (TooManyListenersException e) {
            log.error(e.getMessage());
            throw new TooManyListeners();
        }
    }

    /**
     * 关闭串口
     */
    public void closePort() {
        log.error("关闭串口");
        if (serialPort != null) {
            serialPort.close();
        }
    }
}
